package com.legendframework.core.ioc;

import com.legendframework.core.Store;
import com.legendframework.core.aop.utils.AopUtils;
import com.legendframework.core.ioc.annotation.Bean;
import com.legendframework.core.ioc.annotation.Resource;
import com.legendframework.core.ioc.exception.BeanNotOfRequiredTypeException;
import com.legendframework.core.ioc.exception.BeansException;
import com.legendframework.core.ioc.exception.NoSuchBeanDefinitionException;
import com.legendframework.core.ioc.exception.NoUniqueBeanDefinitionException;
import com.legendframework.core.ioc.pojo.GenerateBeanParam;
import com.legendframework.core.AbstractLegendPlugin;
import com.legendframework.core.dao.Return;
import com.legendframework.core.support.lang3.StringUtils;

import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class DefaultBeansFactory implements IocPool, BeansFactory {

    /**
     * Bean容器
     */
    private final Map<String, Object> beans = new ConcurrentHashMap<>();

    /**
     * Bean创建中容器
     * 存放那些Bean还在创建中的对象，如果创建完成，这个对象会在这个容器中移除
     */
    private final Map<String, Object> createdBeans = new ConcurrentHashMap<>();

    /**
     * 模板池
     * <p>
     * Map<模板类,方法Bean类型的执行器>
     * <p>
     * 如果Method为NULL 则代表KEY类型为@Component(肯定为普通类) , 否则代表为@Bean(有可能Key是抽象类或者接口)
     */
    private final Map<Class<?>, GenerateBeanParam> classPool = new HashMap<>();

    /**
     * Bean工厂实例
     * 提供类Bean创建的功能
     */
    private final Store store;

    public DefaultBeansFactory(Store store) {
        this.store = store;
    }

    public Map<String, Object> getCreatedBeans() {
        return createdBeans;
    }

    @Override
    public void addClassPool(Class<?> cls , Method method , Object obj , AbstractLegendPlugin plugin) {
        if (cls == null) {
            classPool.put(method.getReturnType(),new GenerateBeanParam(method.getReturnType(),plugin,method,obj));
        }else {
            classPool.put(cls,new GenerateBeanParam(cls,plugin));
        }
    }

    /**
     * 将对象放入核心容器中
     * @param bean 对象
     * @param beanName 对象名称
     * @return
     */
    public <T> Return<String,T> toIoc(T bean , String beanName){
        beanName = StringUtils.isEmpty(beanName) ? bean.getClass().getName() : beanName;
        beans.put(beanName, bean);
        return Return.build(beanName,bean);
    }

    /**
     * 根据一个类型获取他的子实现类的Bean生成方式
     * <p>
     * 入参可能是一个接口或者抽象类
     *
     * @param cls cls
     * @return 对应生成方式的所需参数 如果这个@Resource不是必须的且没有找到，则返回NULL
     */
    @Override
    public GenerateBeanParam getBeanTypeByClass(Class<?> cls , Resource resource , AbstractLegendPlugin plugin) {
        if (classPool.containsKey(cls)) {
            GenerateBeanParam generateBeanParam = classPool.get(cls);
            if (generateBeanParam == null) {
                return new GenerateBeanParam(cls,plugin);
            } else {
                return new GenerateBeanParam(cls,generateBeanParam.getPlugin(),generateBeanParam.getMethod(),generateBeanParam.getObject());
            }
        }
        //如果池没有这个key可能因为入参是一个高层接口
        //根据@Bean方法类型生成的对象集合
        List<GenerateBeanParam> byBean = classPool.keySet().stream()
                .filter(cls::isAssignableFrom)
                .filter(i -> classPool.get(i) != null && classPool.get(i).getMethod() != null)
                .map(classPool::get)
                .collect(Collectors.toList());
        //根据@Component类型方式生成对象的集合
        List<GenerateBeanParam> byComponent = classPool.keySet().stream()
                .filter(cls::isAssignableFrom)
                .filter(i -> classPool.get(i) != null && classPool.get(i).getMethod() == null)
                .map(classPool::get)
                .collect(Collectors.toList());
        //排错
        if (byBean.size() + byComponent.size() > 1 && StringUtils.isEmpty(resource.name()) && resource.required()) {
            Set<Class> l = byBean.stream().map(GenerateBeanParam::getCls).collect(Collectors.toSet());
            l.addAll(byComponent.stream().map(GenerateBeanParam::getCls).collect(Collectors.toSet()));
            throw new NoUniqueBeanDefinitionException("找到多个Bean的实现: {"+StringUtils.join(l.iterator(),",")+"}");
        }
        if (!resource.required() && byBean.size() + byComponent.size() > 1) {
            return null;
        }
        if (byBean.size() + byComponent.size() >= 1){
            GenerateBeanParam r = null;
            for (GenerateBeanParam generateBeanParam : byBean) {
                Bean annotation = generateBeanParam.getMethod().getAnnotation(Bean.class);
                if (StringUtils.equals(resource.name(), annotation.value())) {
                    r = generateBeanParam;
                    break;
                }
            }
            if (r == null) {
                for (GenerateBeanParam generateBeanParam : byComponent) {
                    String value = null;
                    for (BeansGenerate beansGenerate : store.getBeansFactoryManager().getBeansGenerates()) {
                        if (generateBeanParam.getCls().isAnnotationPresent(beansGenerate.getAnnotation())) {
                            value = beansGenerate.getBeanName(generateBeanParam.getCls());
                            break;
                        }
                    }
                    if (StringUtils.equals(resource.name(), value)) {
                        r = generateBeanParam;
                    }
                }
            }
            if (r == null) {
                if (byBean.size() + byComponent.size() == 1 && StringUtils.isEmpty(resource.name())){
                    if (byBean.isEmpty()) {
                        r = byComponent.get(0);
                    }else {
                        r = byBean.get(0);
                    }
                }
            }
            return r;
        }
        if (!resource.required()) {
            return null;
        }
        throw new NoSuchBeanDefinitionException("没有找到这个Bean： class:'"+cls.getName()+"' ,beanName: '"+resource.name()+"'");
    }

    @Override
    public Object getBean(String name) throws BeansException {
        //查找这个name
        List<String> l = beans.keySet().stream().filter(key -> StringUtils.equals(name, key)).collect(Collectors.toList());
        if (l.size() == 0){
            throw new NoSuchBeanDefinitionException("没有找到这个Bean： '"+String.valueOf(name)+"'");
        }
        if (l.size() > 1) {
            throw new NoUniqueBeanDefinitionException("找到多个Bean的实现: '"+String.valueOf(name)+"' , {"+StringUtils.join(l.iterator(),",")+"}");
        }
        return beans.get(l.get(0));
    }

    /**
     * 获取指定Bean实例并按照指定的类型返回
     * 请确保Bean能够强制转换为传入的 requiredType 类型
     *
     * @param name Bean的名称
     * @param requiredType 返回的类型
     * @return 对应的Bean
     * @throws NoSuchBeanDefinitionException 如果没有找到这个Bean
     * @throws BeanNotOfRequiredTypeException 类型错误异常
     * @throws BeansException 其他Bean逻辑失败异常
     */
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException{
        return getBeanAndName(name,requiredType).getR2();
    }

    /**
     * 获取指定Bean实例并按照指定的类型返回
     * 请确保Bean的这个requiredType类型下只有唯一一个Bean实例
     *
     * @param requiredType 查找的Bean类型
     * @return 对应的Bean
     * @throws NoSuchBeanDefinitionException 如果没有找到这个Bean
     * @throws BeanNotOfRequiredTypeException 类型错误异常
     * @throws NoUniqueBeanDefinitionException 实例不唯一异常
     * @throws BeansException 其他Bean逻辑失败异常
     */
    public <T> T getBean(Class<T> requiredType) throws BeansException{
        return getBeanAndName(requiredType).getR2();
    }

    public <T> Return<String, T> getBeanAndName(String name, Class<T> requiredType) throws BeansException {
        if (requiredType == null) {
            throw new BeansException("类型不能为空");
        }
        if (StringUtils.isNotEmpty(name)) {
            Object bean = getBean(name);
            if (!requiredType.isAssignableFrom(bean.getClass()) && !requiredType.isAssignableFrom(AopUtils.getTargetClass(bean))) {
                throw new BeanNotOfRequiredTypeException(name,requiredType,bean.getClass());
            }
            return Return.build(name,(T) bean);
        } else {
            return getBeanAndName(requiredType);
        }
    }

    public <T> Return<String, T> getBeanAndName(Class<T> requiredType) throws BeansException {
        List<Return<String, Object>> l = beans.keySet()
                .stream()
                .filter(key -> requiredType.isAssignableFrom(beans.get(key).getClass()) || requiredType.isAssignableFrom(AopUtils.getTargetClass(beans.get(key))))
                .map(key -> Return.build(key, beans.get(key))).collect(Collectors.toList());
        if (l.size() == 0){
            throw new NoSuchBeanDefinitionException("没有找到这个Bean： '"+requiredType.getName()+"'");
        }
        if (l.size() > 1) {
            throw new NoUniqueBeanDefinitionException("找到多个Bean的实现: '"+requiredType.getName()+"' , {"+
                    StringUtils.join(l.stream().map(Return::getR2).collect(Collectors.toList()).iterator(),",")+"}");
        }
        return (Return<String, T>) l.get(0);
    }
}
